﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

[assembly: PreApplicationStartMethod(typeof(MVCPlugin.Utility.Application), "Initialize")]
namespace MVCPlugin.Utility
{    
    public class Application
    {
        internal static Application Instance;
        private static object s_SyncObj = new object();
        private static ILogger Logger = new ConsoleLogger();

        public static string BinFolderName
        {
            get;
            private set;
        }

        public static void Initialize()
        {
            Initialize("bin");
        }

        public static void Initialize(string binFolderName)
        {
            BinFolderName = binFolderName;
            if (Instance == null)
            {
                lock (s_SyncObj)
                {
                    if (Instance == null)
                    {
                        try
                        {
                            Application tmp = null;
                            string typeName = ConfigurationManager.AppSettings["applicationTypeName"];
                            if (string.IsNullOrWhiteSpace(typeName) == false)
                            {
                                try
                                {
                                    Type type = Type.GetType(typeName, false);
                                    if (type != null)
                                    {
                                        tmp = Activator.CreateInstance(type) as Application;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(ex);
                                    tmp = null;
                                }
                            }
                            if (tmp == null)
                            {
                                Instance = new Application();
                            }
                            else
                            {
                                Instance = tmp;
                            }
                           
                            Plugin.ClearShadowCopiedFile();
                            Plugin.Initialize(Instance);
                           
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex);
                        }
                    }
                }
            }
        }

        internal void DoStart(HttpApplication app)
        {
            Start(app);
        }

        internal void DoStop(HttpApplication app)
        {
            Stop(app);
        }

        internal void DoError(Exception ex, HttpApplication app)
        {
            OnError(new ErrorEventArgs(ex) { HttpApplication = app });
        }

        #region Start
        protected virtual void Start(HttpApplication app)
        {
            OnStarting(new ApplicationEventArgs { HttpApplication = app });
            GlobalFilters.Filters.Add(new RequestHookAttribute(this.OnBeginRequest, this.OnEndRequest));
            RegisterPluginRoute(app);
            OnStarted(new ApplicationEventArgs { HttpApplication = app });
        }

        public event EventHandler<ApplicationEventArgs> Starting;
        protected virtual void OnStarting(ApplicationEventArgs e)
        {
            var handler = Starting;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler<ApplicationEventArgs> Started;
        protected virtual void OnStarted(ApplicationEventArgs e)
        {
            var handler = Started;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        #endregion

        #region Stop
        protected virtual void Stop(HttpApplication app)
        {
            OnStopping(new ApplicationEventArgs { HttpApplication = app });
            OnStopped(new ApplicationEventArgs { HttpApplication = app });
        }

        public event EventHandler<ApplicationEventArgs> Stopping;
        protected virtual void OnStopping(ApplicationEventArgs e)
        {
            var handler = Stopping;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler<ApplicationEventArgs> Stopped;
        protected virtual void OnStopped(ApplicationEventArgs e)
        {
            var handler = Stopped;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        #endregion

        #region RegisterPluginRoute

        public event EventHandler<EnableOrNotEventArgs> PluginRouteRegistering;
        protected virtual void OnPluginRouteRegistering(EnableOrNotEventArgs e)
        {
            var handler = PluginRouteRegistering;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler<ApplicationEventArgs> PluginRouteRegistered;
        protected virtual void OnPluginRouteRegistered(ApplicationEventArgs e)
        {
            var handler = PluginRouteRegistered;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void RegisterPluginRoute(HttpApplication app)
        {
            EnableOrNotEventArgs e = new EnableOrNotEventArgs() { HttpApplication = app, Enabled = true };
            OnPluginRouteRegistering(e);
            if (e.Enabled)
            {
                Plugin.RegisterRoute(RouteTable.Routes);
            }
            OnPluginRouteRegistered(e);
        }

        #endregion
        

        public event EventHandler<ErrorEventArgs> Error;
        protected virtual void OnError(ErrorEventArgs e)
        {
            var handler = Error;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private class RequestHookAttribute : ActionFilterAttribute
        {
            private Action m_BeforeAction;
            private Action m_AfterAction;
            public RequestHookAttribute(Action beforeAction, Action afterAction)
            {
                m_BeforeAction = beforeAction;
                m_AfterAction = afterAction;
            }
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                if (m_BeforeAction != null)
                {
                    m_BeforeAction();
                }
                base.OnActionExecuting(filterContext);
            }

            public override void OnActionExecuted(ActionExecutedContext filterContext)
            {
                base.OnActionExecuted(filterContext);
                if (m_AfterAction != null)
                {
                    m_AfterAction();
                }
            }
        }

        #region Request

        public event EventHandler BeginRequest;
        protected virtual void OnBeginRequest()
        {
            var handler = BeginRequest;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public event EventHandler EndRequest;
        protected virtual void OnEndRequest()
        {
            var handler = EndRequest;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        #endregion
    }

    public class ApplicationEventArgs : EventArgs
    {
        public HttpApplication HttpApplication { get; internal set; }
    }

    public class EnableOrNotEventArgs : ApplicationEventArgs
    {
        public bool Enabled { get; set; }
    }

    public class ErrorEventArgs : ApplicationEventArgs
    {
        public ErrorEventArgs(Exception ex)
        {
            Exception = ex;
        }
        public Exception Exception { get; private set; }
    }
}
